﻿Imports System.Data.SqlClient
Imports System.Globalization
Imports System.IO
Imports System.Security.Cryptography


Public Class Comun


    '*********************************************************************************
    '********* ELEMENTOS DE CONTROL DE SESIÓN COMUNES A TODOS LOS .aspx **************
    '*********************************************************************************


    'Nombre de la variable de sesión donde se almacenan los permisos de
    'acceso para el usuario que ha iniciado la sesión
    'Se rellena en login.aspx
    Public Const _VARIABLE_PERMISOS As String = "__PERMISOS_ACCESO"

    'Establece, para la web pasada como objeto response, un
    'mecanismo de refresco automático periódico igual al tiempo
    'de estado de sesion menos 5 segundos
    Public Shared Sub establecer_auto_refresco(ByRef session As HttpSessionState, _
                                              ByRef response As HttpResponse)
        Dim tiempo_refresco As String = String.Empty

        tiempo_refresco = Convert.ToString(Convert.ToString(session.Timeout * 60 - 5))
        response.AppendHeader("Refresh", tiempo_refresco)

    End Sub

    'Establece, para la web pasada como objeto response, un
    'mecanismo de cierre de sesión automático establecido en el tiempo
    'de duración de la sesión, menos 5 segundos
    Public Shared Sub establecer_cierre_automatico(ByRef session As HttpSessionState, _
                                                   ByRef request As HttpRequest, _
                                                   ByRef response As HttpResponse)
        Dim tiempo_refresco As String = String.Empty
        Dim dominio As String = String.Empty
        Dim protocolo As String = String.Empty
        Dim destino As String = String.Empty
        Dim subdirectorio As String = String.Empty

        'limpia_sesion(session)

        dominio = request.Url.Host
        protocolo = request.Url.Scheme
        subdirectorio = Comun.sacar_directorio(request.Url.GetComponents(UriComponents.Path, UriFormat.UriEscaped))

        'Si el protocolo del login es por https, se redirecciona
        'a http automáticamente para que no haya problemas con las cabeceras
        'de https
        If protocolo.Equals("http") Then
            destino = "https://" & dominio & "/" & subdirectorio & "/default.aspx"
        Else
            destino = "~/default.aspx"
        End If

        tiempo_refresco = Convert.ToString(Convert.ToString(session.Timeout * 60 - 5))
        response.AppendHeader("Refresh", tiempo_refresco & "; URL=" & destino)
    End Sub

    'Limpia el estado de la sesión, a partir de los objetos de Session
    Public Shared Sub limpia_sesion(ByRef session As HttpSessionState)
        session.Contents.RemoveAll()
        session.Clear()
    End Sub

    'Si la aplicación se ha desplegado en algún directorio, 
    'devuelve el nombre
    'P.ej.: https://pruebas.upsam.es/GestionHorarios/login.aspx
    'devolveria GestionHorarios (sólo se le pasaría GestionHorarios/login.aspx)
    'El argumento que se le debe pasar es siempre:
    'Request.Url.GetComponents(UriComponents.Path, UriFormat.UriEscaped)
    Public Shared Function sacar_directorio(ByVal url_sin_servidor As String) As String
        Dim subdirectorio As String = String.Empty
        Dim ruta As String = String.Empty
        Dim separador As Char() = "/"
        Dim cadenas As String()

        ruta = url_sin_servidor
        cadenas = ruta.Split(separador, 10)
        'Si sólo sale una cadena, es que
        'no hay subdirectorios
        If cadenas.Length >= 2 Then
            subdirectorio = cadenas(0)
        End If

        Return subdirectorio
    End Function

    'Procedimiento que se invoca cada vez que se quiera salir de la aplicación
    'por el motivo que sea: con cerrar, por error, o por cualquier otra cuestión
    'Elimina todo el estado de la sesión y redirecciona a login.aspx en https
    Public Shared Sub salir(ByRef session As HttpSessionState, _
                            ByRef request As HttpRequest, _
                            ByRef response As HttpResponse)

        Dim dominio As String = String.Empty
        Dim protocolo As String = String.Empty
        Dim destino As String = String.Empty
        Dim subdirectorio As String = String.Empty

        'No limpiamos aquí la sesión porque se hace la Default.vb
        'a cada nuevo inicio de sesión
        'limpia_sesion(session)

        dominio = request.Url.Host
        protocolo = request.Url.Scheme
        subdirectorio = Comun.sacar_directorio(request.Url.GetComponents(UriComponents.Path, UriFormat.UriEscaped))

        ''Si el protocolo del login es por https, se redirecciona
        ''a http automáticamente para que no haya problemas con las cabeceras
        ''de https
        'If protocolo.Equals("http") Then
        '    destino = "https://" & dominio & "/" & subdirectorio & "/default.aspx"
        'Else
        '    destino = "~/default.aspx"
        'End If

        destino = "~/default.aspx"
        response.Redirect(destino)
    End Sub

    'Genera una clave SHA512 a partir de la contraseña, de manera que en la
    'base de datos no se guarden las contraseñas en claro
    Public Shared Function generaSHA512(ByVal password As String) As String
        'Crea un objeto de codificación para asegurarnos una codificación estándar
        'para el texto fuente (password)
        Dim codificacionUnicode As New UnicodeEncoding()
        'Recupera una array de Bytes a partir del texto fuente
        Dim textoFuente() As Byte = codificacionUnicode.GetBytes(password)
        'Instanciamos un objeto SHA512 Provider
        Dim SHA512 As New SHA512CryptoServiceProvider()

        'Calculamos el valor Hash a partir del fuete
        Dim claveHash() As Byte = SHA512.ComputeHash(textoFuente)


        'Y lo convertimos en String para devolverlo
        Return Convert.ToBase64String(claveHash)

    End Function

    'Genera una clave MD5 a partir de la contraseña, de manera que en la
    'base de datos no se guarden las contraseñas en claro
    Public Shared Function generaMD5(ByVal password As String) As String
        'Crea un objeto de codificación para asegurarnos una codificación estándar
        'para el texto fuente (password)
        Dim codificacionUnicode As New UnicodeEncoding()
        'Recupera una array de Bytes a partir del texto fuente
        Dim textoFuente() As Byte = codificacionUnicode.GetBytes(password)
        'Instanciamos un objeto MD5 Provider
        Dim Md5 As New MD5CryptoServiceProvider()
        'Calculamos el valor Hash a partir del fuete
        Dim claveHash() As Byte = Md5.ComputeHash(textoFuente)


        'Y lo convertimos en String para devolverlo
        Return Convert.ToBase64String(claveHash)

    End Function

    '*********************************************************************************





    '*********************************************************************************
    '********* FACILITADORES PARA RELLENO DE DROP DOWN LIST's ************************
    '*********************************************************************************

    'Rellena un DropDownList con todos los cursos académicos desde 2000/01 hasta 2098/99
    'Argumentos:
    '   - texto_nulo: propiedad .text para la primera opción del DDL P.ej.: <Todos>
    '   - valor_nulo: propiedad .value del DDL para la primera opción del DDL P.ej.: todos
    'El valor de los curso es el número de año más alto dentro del curso académico. Por ejemplo,
    'para el curso 2011/2012, el valor es 2012
    Public Shared Sub Rellenar_DDLTodosCursos(ByRef ddl As DropDownList, _
                                            ByVal texto_nulo As String, ByVal valor_nulo As String)
        Dim año As Integer
        Dim curso As String

        ddl.Items.Add(New ListItem(texto_nulo, valor_nulo))

        For año = 2000 To 2098
            curso = año.ToString & "/" & (año + 1).ToString().Substring(2)
            ddl.Items.Add(New ListItem(curso, (año + 1).ToString()))
        Next

        ddl.DataBind()

        'Seleccionamos automáticamente el año en curso para evitar
        'que el usuario tenga que buscarlo por sí solo
        'Si la fecha de hoy es de los meses del principio del curso académico
        If Today().Month >= 10 And Today.Month() <= 12 Then
            ddl.SelectedValue = Today().Year() + 1
        Else 'si es de los del final
            ddl.SelectedValue = Today().Year()
        End If
    End Sub

    'Rellena un DropDownList con todos los cursos académicos desde 2000/01 hasta 2098/99
    'Argumentos:
    '   - texto_nulo: propiedad .text para la primera opción del DDL P.ej.: <Todos>
    '   - valor_nulo: propiedad .value del DDL para la primera opción del DDL P.ej.: todos
    'El valor de los curso es el número de año más alto dentro del curso académico. Por ejemplo,
    'para el curso 2011/2012, el valor es 2012
    'No da la opción de poner valor nulo como primera opción
    Public Shared Sub Rellenar_DDLTodosCursos_SinNulos(ByRef ddl As DropDownList)
        Dim año As Integer
        Dim curso As String

        For año = 2000 To 2098
            curso = año.ToString & "/" & (año + 1).ToString().Substring(2)
            ddl.Items.Add(New ListItem(curso, (año + 1).ToString()))
        Next

        ddl.DataBind()

        'Seleccionamos automáticamente el año en curso para evitar
        'que el usuario tenga que buscarlo por sí solo
        'Si la fecha de hoy es de los meses del principio del curso académico
        If Today().Month >= 10 And Today.Month() <= 12 Then
            ddl.SelectedValue = Today().Year() + 1
        Else 'si es de los del final
            ddl.SelectedValue = Today().Year()
        End If
    End Sub


    'Rellena cualquier DropDownList pasado como primer parámetro a partir de la consulta indicada como segundo
    'parámetro, tomando como valor del DropDownList del campo de la consulta indicado como tercer parámetro
    'y como texto el campo de la consulta indicado como cuarto parámetro
    'Desde la Base de Datos referenciada por la cadena de conexión cnxHorarios
    Public Shared Sub Rellenar_DDL(ByRef ddl As DropDownList, ByVal consulta As String, _
                                   ByVal campo_valor As String, ByVal campo_texto As String, _
                                   ByVal texto_nulo As String, ByVal valor_nulo As String)

        Dim conexion As SqlConnection
        Dim adaptador As SqlDataAdapter
        Dim ds As DataSet = New DataSet
        Dim afectados As Integer = 0

        Try
            'obtiene la cadena de conexión a la base de datos del fichero Web.config
            conexion = New SqlConnection(ConfigurationManager.ConnectionStrings("cnxHorarios").ConnectionString())

            'creamos un adaptador para la consulta y conexión deseadas
            adaptador = New SqlDataAdapter(consulta, conexion)
            'rellenamos el objeto DataSet a partir del SQLDataAdapter
            afectados = adaptador.Fill(ds)

            If afectados = 0 Then
                ddl.Items.Add(New ListItem("(Sin datos)", "sin_datos"))

            Else
                'vaciamos el DropDownList
                ddl.Items.Clear()
                'Añadimos un primer elemento nulo
                ddl.Items.Add(New ListItem(texto_nulo, valor_nulo))

                'rellenamos cada elemento del DropDownList a partir de la tabla
                'del DataSet relleno.
                'Asignamos el el texto y valor que correspondan con los campos indicados como parámetro
                'al procedimiento
                For Each e As DataRow In ds.Tables(0).Rows
                    ddl.Items.Add(New ListItem(e.Item(campo_texto), e.Item(campo_valor)))
                Next

            End If

            'vinculamos los datos
            ddl.DataBind()

        Catch ex As Exception
            'si hubiera algún problema con el acceso a la base de datos, o cualquier otro indicente
            'que hiciera que no se pueda rellenar a patir de los datos almacenados en la base de datos,
            'se rellena con un miembro "NO DISPONIBLE"
            ddl.Items.Clear()
            ddl.Items.Add(New ListItem("(No disponible)", valor_nulo))
            ddl.DataBind()
        End Try
    End Sub


    'Rellena cualquier DropDownList pasado como primer parámetro a partir de la consulta indicada como segundo
    'parámetro, tomando como valor del DropDownList del campo de la consulta indicado como tercer parámetro
    'y como texto el campo de la consulta indicado como cuarto parámetro
    Public Shared Sub Rellenar_DDL_SinNulos(ByRef ddl As DropDownList, ByVal consulta As String, _
                                   ByVal campo_valor As String, ByVal campo_texto As String)

        Dim conexion As SqlConnection
        Dim adaptador As SqlDataAdapter
        Dim ds As DataSet = New DataSet
        Dim afectados As Integer = 0

        Try
            'obtiene la cadena de conexión a la base de datos del fichero Web.config
            conexion = New SqlConnection(ConfigurationManager.ConnectionStrings("cnxBD").ConnectionString())

            'creamos un adaptador para la consulta y conexión deseadas
            adaptador = New SqlDataAdapter(consulta, conexion)
            'rellenamos el objeto DataSet a partir del SQLDataAdapter
            afectados = adaptador.Fill(ds)

            'vaciamos el DropDownList
            ddl.Items.Clear()

            If afectados = 0 Then
                ddl.Items.Add(New ListItem("(N/D)", "sin_datos"))
            Else
                'rellenamos cada elemento del DropDownList a partir de la tabla
                'del DataSet relleno.
                'Asignamos el el texto y valor que correspondan con los campos indicados como parámetro
                'al procedimiento
                For Each e As DataRow In ds.Tables(0).Rows
                    ddl.Items.Add(New ListItem(e.Item(campo_texto), e.Item(campo_valor)))
                Next

            End If

            'vinculamos los datos
            ddl.DataBind()
            ddl.SelectedIndex = 0

        Catch ex As Exception
            'si hubiera algún problema con el acceso a la base de datos, o cualquier otro indicente
            'que hiciera que no se pueda rellenar a patir de los datos almacenados en la base de datos,
            'se rellena con un miembro "NO DISPONIBLE"
            ddl.Items.Clear()
            ddl.DataBind()
        End Try
    End Sub


    'Vacía y deshabilita un DropDownList y su Label asociada
    Public Shared Sub Limpiar_Control(ByRef ddl As DropDownList, ByRef lbl As Label, _
                                                 ByVal texto_nulo As String, ByVal valor_nulo As String)
        ddl.Items.Clear()
        ddl.Items.Add(New ListItem(texto_nulo, valor_nulo))
        ddl.Enabled = False
        lbl.Enabled = False
    End Sub

    '*********************************************************************************


    '*********************************************************************************
    '******* FUNCIONES AUXILIARES DE COMPROBACIÓN, CÁLCULOS Y FORMATOS ***************
    '*********************************************************************************

    'Verifica si unas fechas de inicio y fin tienen el orden adecuado,
    'de lo contrario muestrar un mensaje de error
    Public Shared Function revisar_fechas(ByRef TBFechaInicio As TextBox, ByRef TBFechaFin As TextBox, _
                                      ByRef LblMensajeError As Label) As Boolean
        Dim fecha_inicio As Date
        Dim fecha_fin As Date
        Dim correctas As Boolean = True

        LblMensajeError.Visible = False

        'si cuando cambia la fecha de inicio, ambas fechas tienen algo
        If Not String.IsNullOrEmpty(TBFechaInicio.Text) And _
            Not String.IsNullOrEmpty(TBFechaFin.Text) Then

            Try
                fecha_inicio = Convert.ToDateTime(TBFechaInicio.Text)
                fecha_fin = Convert.ToDateTime(TBFechaFin.Text)

                'si la fecha de inicio es posterior a la de fin
                If fecha_inicio > fecha_fin Then
                    'mostramos un mensaje de error y ponemos el foco
                    'en el TextBox del la Fecha de Inicio
                    LblMensajeError.Visible = True
                    correctas = False
                End If
            Catch ex As Exception
                correctas = False
            End Try

        End If

        Return correctas
    End Function

    'Devuelve true si el año de la fecha del argumento es bisiesto,
    Public Shared Function esBisiesto(ByVal fecha As Date) As Boolean
        esBisiesto = Day(DateSerial(Year(fecha), 3, 0)) = 29
    End Function

    'Devuelve una fecha en formato dd/mm/aaaa hh:mm:ss
    'Necesario para insertar fechas correctamente en una bbdd en Español
    Public Shared Function convertir_formato_fecha(ByVal fecha As Date) As String
        Dim fecha_formato_nuevo As String = String.Empty

        fecha_formato_nuevo = Format(fecha, "dd/MM/yyyy HH:mm:ss")

        Return fecha_formato_nuevo

    End Function


    Public Shared Function convertir_formato_moneda(ByVal valor As Double) As String
        Dim valor_formato_enUS As String = String.Empty
        Dim nfi As NumberFormatInfo = New CultureInfo("en-US", False).NumberFormat

        valor_formato_enUS = valor.ToString("N", nfi)

        Return valor_formato_enUS
    End Function

    'Devuelve el número de días que difieren respecto del lunes
    'el día de la semana que se psa por argumento
    Private Shared Function diferencia_con_lunes(ByVal dia_semana As DayOfWeek) As Integer
        Dim diferencia As Integer = 0

        If dia_semana.Equals(DayOfWeek.Monday) Then
            diferencia = 0
        ElseIf dia_semana.Equals(DayOfWeek.Tuesday) Then
            diferencia = 1
        ElseIf dia_semana.Equals(DayOfWeek.Wednesday) Then
            diferencia = 2
        ElseIf dia_semana.Equals(DayOfWeek.Thursday) Then
            diferencia = 3
        ElseIf dia_semana.Equals(DayOfWeek.Friday) Then
            diferencia = 4
        ElseIf dia_semana.Equals(DayOfWeek.Saturday) Then
            diferencia = 5
        ElseIf dia_semana.Equals(DayOfWeek.Sunday) Then
            diferencia = 6
        End If

        Return diferencia
    End Function


    'Devuelve la fecha del lunes de la semana a la que corresponde el día
    'pasado por argumento.
    'Se le puede pasar cualquiera de los días de la semana y devuelve simpre
    'el lunes correspondiente.
    Public Shared Function fecha_lunes_semana(ByVal dia_semana As Date) As Date
        Dim fecha_lunes As Date = Date.MinValue
        'Día del año numérico
        Dim diaAño As Integer = dia_semana.DayOfYear
        'Dia base del año (día juliano para el día 1 de enero del año de la fecha)
        Dim diaBase As New DateTime(dia_semana.Year, 1, 1)


        fecha_lunes = diaBase.AddDays(diaAño - diferencia_con_lunes(dia_semana.DayOfWeek) - 1)

        Return fecha_lunes
    End Function

    'Comprueba si la letra del DNI se corresponde con el número introducido.
    'Si es correcta devuelve true, si no devuelve false
    Public Shared Function letra_dni_correcta(ByVal numero As String, ByVal letra As String) As Boolean
        Dim letra_correcta As String = Mid$("TRWAGMYFPDXBNJZSQVHLCKE", (numero Mod 23) + 1, 1)
        Return (letra.Equals(letra_correcta))
    End Function

    'rellena a 0's por la izquierda una cadena hasta el número de posiciones indicada
    'en el segundo argumento.
    Public Shared Function rellenar_con_ceros(ByVal cadena As String, ByVal numero_posiciones As Byte)
        Dim cadena_rellena As String

        cadena_rellena = New String(cadena)

        'si la longitud de la cadena tiene menos posiciones de las deseadas
        'se rellenar a 0 por la izquierda hasta alcanzar dicha longitud
        For i As Integer = cadena.Length To numero_posiciones - 1
            cadena_rellena = "0" & cadena_rellena
        Next

        Return cadena_rellena

    End Function

    'Convierte una cadena variable en una de tamaño fijo. Si es mayor al número de posiciones
    'la trunca, si no, la rellena con espacios a la derecha hasta llegar a la longitud dada.
    'También convierte la cadena a mayúsculas
    Public Shared Function cadena_tamaño_fijo(ByVal cadena As String, ByVal numero_posiciones As Byte)
        Dim cadena_rellena As String

        cadena_rellena = New String(cadena)

        'si el tamaño de la cadena excede el número de posiciones
        'se trunca
        If cadena.Length > numero_posiciones Then
            cadena_rellena = cadena.Remove(numero_posiciones, cadena.Length - numero_posiciones)
        Else 'si no, se rellena con espacios a la derecha
            For i As Integer = cadena.Length To numero_posiciones - 1
                cadena_rellena = cadena_rellena & " "
            Next
        End If

        Return cadena_rellena.ToUpper()

    End Function

    'Elimina todos los caracteres de una cadena que coincidan con el segundo argumento
    Public Shared Function elimina_caracter_cadena(ByVal cadena As String, ByVal caracter As Char) As String
        Dim cadena_nueva As String = String.Empty

        For i = 0 To cadena.Length - 1
            If cadena(i) <> caracter Then
                cadena_nueva = cadena_nueva & cadena(i)
            End If
        Next

        Return cadena_nueva

    End Function


    'Consturye un código único para un horario a partir de los argumentes. El forma,
    'será dependerá del tipo de clase. Según los siguiente:
    '   - Si es de Teoría: CCCCUUPTVG, donde:
    '       · CCCC: Curso académico en 4 cifras. Por ejemplo: 2012
    '       · UU: Tipo de Clase en 4 cifras. Para teoría, siempre: TE
    '       · P: Tipo de periodo en 1 cifra. Por ejemplo, si es primer cuatrimestre: 1
    '       · T: Tipo de Horario en 1 cifra. Por ejemplo, 1 - Horario, 2 - Plantilla, 3 - Publicado
    '       · V: Número de versión. Con N cifras. Si es la versión 1, pues 1. Si fuera la 20, pues 20.
    '       · G: Código de grupo. Con N cifras.
    '       Por ejemplo, para un Horario del 2012, de Teoría, para el primer cuatrimestre, que no sea plantilla,
    '       que esté publicado y que sea la 3ª versión, para el grupo EGT25: 2012TE1013EGT25

    '   - Si es de Laboratorio o Tutorías: CCCCUUPTVAAAAMMDD, todo igual, pero se le añader:
    '       · Dado que los laboratorios van por semanas y no por cuatrimestres, en el campo P (Período),
    '           se pondrá siempre 3=Anual
    '       · la fecha del lunes de la semana a la que se corresponde en formato:
    '           AAAA: año en cuatro cifras
    '           MM: mes en dos cifras
    '           DD: día del mes en dos cifras
    '       Por ejemplo, para un Horario del 2011, de Laboratorios, para el segundo cuatrimestre,
    '       que sea una plantilla, no pulbicada en su 2ª versión para la semana del 11 de abril de 2011,
    '       sería: 2011LA210220110411
    Public Shared Function construye_codigo_horario(ByVal curso_academico As String, _
                                                    ByVal codigo_tipo_clase As String, _
                                                    ByVal codigo_periodo As String, _
                                                    ByVal codigo_tipo_horario As String, _
                                                    ByVal numero_version As Integer, _
                                                    ByVal fecha_lunes_semana As Date, _
                                                    ByVal codigo_grupo As String)
        Dim es_plantilla As String = "0"
        Dim esta_publicado As String = "0"

        Dim codigo_horario As String = String.Empty



        codigo_horario = curso_academico & _
                         codigo_tipo_clase & _
                         codigo_periodo & _
                         codigo_tipo_horario & _
                         es_plantilla & _
                         esta_publicado & _
                         numero_version


        If codigo_tipo_clase.Equals("TE") Then
            'Si es de un horario de teoría, además
            'le añadimos el código del grupo que
            'corresponde
            codigo_horario = codigo_horario & String.Format("{0:yyyyMMdd}", fecha_lunes_semana)
        Else
            'Si es de un horario que no sea de teoría, además
            'le añadimos la fecha del lunes de la semana a la que
            'corresponde
            codigo_horario = codigo_horario & String.Format("{0:yyyyMMdd}", fecha_lunes_semana)
        End If

        Return codigo_horario
    End Function


    '*********************************************************************************



    '*********************************************************************************
    '******* FUNCIONES CURSO ACADÉMICO ***********************************************
    '*********************************************************************************

    'Devuelve en los dos argumentos del procedimiento como String
    'las fechas INICIAL y FINAL para el curso académico ACTUAL
    'Para ello se basa en la fecha del sistema
    Public Shared Sub obtener_fechas_curso_academico(ByRef fecha_inicial As String, ByRef fecha_final As String)
        Dim fi As String
        Dim ff As String

        If (Today.Month >= 10 And Today.Month <= 12) Then
            fi = "1/10/" & Today.Year().ToString()
            ff = "30/9/" & (Today.Year() + 1).ToString()
        Else
            fi = "1/10/" & (Today.Year() - 1).ToString()
            ff = "30/9/" & Today.Year().ToString()
        End If

        fecha_inicial = convertir_formato_fecha(Convert.ToDateTime(fi))
        fecha_final = convertir_formato_fecha(Convert.ToDateTime(ff))
    End Sub

    'Devuelve en los dos argumentos del procedimiento como String
    'las fechas INICIAL y FINAL para el curso académico que empieza
    'en SEPTIEMBRE DEL AÑO NATURAL indicado como primer argumento
    'Para ello se basa en la fecha del sistema
    Public Shared Sub obtener_fechas_curso_academico(ByVal año As Integer, _
                                                     ByRef fecha_inicial As String, ByRef fecha_final As String)
        Dim fi As String
        Dim ff As String

        fi = "1/10/" & (año - 1).ToString()
        ff = "30/9/" & año.ToString()

        fecha_inicial = convertir_formato_fecha(Convert.ToDateTime(fi))
        fecha_final = convertir_formato_fecha(Convert.ToDateTime(ff))
    End Sub

    'Devuelve el año base del curso académico de la fecha pasada por argumento.
    'Por ejemplo, para el curso 2009/2010, el curso base es el 2010. Así que, si
    'pasan como fecha, por ejemplo, 12/10/2009, el año base será 2009, pero si 
    'pasan como fecha el 3/4/2010, también será el año 2009 porque ambas fechas
    'están dentro del curso académico 2009/2010
    Public Shared Function obtener_año_base_curso_academico(ByVal fecha As Date) As Integer
        Dim año_base As Integer = -1

        If fecha.Month >= 10 And fecha.Month <= 12 Then
            año_base = fecha.Year + 1
        Else
            año_base = fecha.Year
        End If

        Return año_base
    End Function

    '*********************************************************************************


End Class